function PackageInfo(name, pkg)
{
	PackageInfo.packageMap[name] = this;
	this.name = name;
	this.pkg = pkg;

	if (this.pkg.name != name)
	{
		this.implementation = this.pkg.name;
		return;
	}
	this.fileInfos = [];

	for (var file in this.pkg.scriptObjectMap)
	{
		var info = new FileInfo(this, file);
		this.fileInfos.push(info);
		this.fileInfos[file] = info;
	}
	this.fileInfos.sort();
}
/**
 * @private
 */
PackageInfo.packageMap = {};
/**
 * @public
 */
PackageInfo.require = function(name)
{
	if (name)
	{
		if (this.packageMap[name])
		{ //return old
			return this.packageMap[name];
		}
		else
		{
			var pkg = $import(name + ":");

			if (pkg)
			{
				return new PackageInfo(name, pkg);
			}
			else
			{
				return null;
			}
		}
	}
	else
	{
		return this.rootInfo;
	}
}

PackageInfo.requireRoot = function(files)
{
	if (this.rootInfo)
	{
		return this.rootInfo;
	}
	else
	{
		return this.rootInfo = new RootInfo(files);
	}
}

var $p = PackageInfo.prototype;

/**
 * @private
 */
$p.getInitializers = function()
{
	function gen(fi)
  {
	  return function()
	  {
		  fi.getObjectInfoMap();
	  }
  }
	var rtvs = [];

	for (var i = 0; i < this.fileInfos.length; i++)
	{
		rtvs.push(gen(this.fileInfos[i]));
	}
	return rtvs;
}

/**
 * @public
 */
$p.getDescription = function()
{
	return this.getSourceEntry().getDescription();
}
/**
 * @public
 */
$p.getSourceEntry = function()
{
	if (! this._sourceParser)
	{
		this._sourceParser = SourceEntry.require("__package__.js", this.pkg.name);
	}
	return this._sourceParser;
}

/**
 * @public
 */
$p.getObjectInfoMap = function()
{
	if (this._objectInfoMap)
	{
		return this._objectInfoMap;
	}
	else
	{
		this._objectInfoMap = {
		};

		for (var i = 0; i < this.fileInfos.length; i++)
		{
			var m = this.fileInfos[i].getObjectInfoMap();

			for (var o in m)
			{
				this._objectInfoMap[o] = m[o];
			}
		}
		return this._objectInfoMap;
	}
};
/**
 * @public
 */
$p.getObjectInfos = function(map)
{
	if (this._objectInfos)
	{
		return this._objectInfos;
	}
	else
	{
		this._objectInfos = [];
		var m = this.getObjectInfoMap();

		for (var n in m)
		{
			this._objectInfos.push(n);
		}
		this._objectInfos.sort();

		for (var i = 0; i < this._objectInfos.length; i++)
		{
			//$log.info(list[i]);
			this._objectInfos[i] = m[this._objectInfos[i]];
		//$log.info(list[i]);
		}
		return this._objectInfos;
	}
}

/**
 * @public
 */
function FileInfo(pkginf, fileName)
{
	this.pkgInfo = pkginf;
	this.pkg = pkginf.pkg;
	this.name = fileName;
	//$log.debug(this.pkg.dependenceMap);
	this.dependences = this.pkg.dependenceMap[fileName] || [];
	this.objects = [];
	var osm = this.pkg.objectScriptMap;

	for (var o in osm)
	{
		if (osm[o] == fileName)
		{
			this.objects.push(o);
		}
	}
}

$p= FileInfo.prototype;

/**
 * @public
 */
$p.getBeforeDependenceInfos = function()
{
	if (! this.beforeDependenceInfos)
	{
		this.beforeDependenceInfos = [];

		for (var i = 0; i < this.dependences.length; i++)
		{
			var di = this.dependences[i];

			if (di.beforeLoad)
			{
				var objects = di.getNames();
				var pkginf = PackageInfo.require(di.pkg2.name);
				var info =
				{
					objects: objects,
					packageName: pkginf.name,
					detail: di
				};

				this.beforeDependenceInfos.push(info);
			//this.beforeDependenceInfos[path] = info;
			}
		}
	//this.beforeDependenceInfos.sort();
	}
	return this.beforeDependenceInfos;
};
/**
 * @public
 */
$p.getAfterDependenceInfos = function(object)
{
	if (! this.afterDependenceMap)
	{
		var forAll = [];
		var forObjectMap = {
		};

		var forAny = [];

		for (var i = 0; i < this.dependences.length; i++)
		{
			var di = this.dependences[i];

			if (! di.loadtime)
			{
				var forObject = null;

				if (di instanceof Array)
				{
					forObject = di[0];
					forObjectMap[forObject] = forObjectMap[forObject] || [];
					di = di[1];
				}
				var objects = di.getNames();
				var pkginf = PackageInfo.require(di.pkg2.name);
				var info =
				{
					objects: objects,
					forObject: forObject,
					packageName: pkginf.name,
					detail: di
				};

				if (forObject)
				{
					forObjectMap[forObject].push(info);
					forAny.push(info);
				}
				else
				{
					forAll.push(info);
				}
			}
		}
		var result = [];
		result.forObjectMap = forObjectMap;

		for (var n in result)
		{
			result[n] = null;
		}
		result.forAll = forAll;
		result.push.apply(result, forAll);

		if (forAny.length > 0)
		{
			result.push.apply(result, forAny);

			for (var n in forObjectMap)
			{
				result[n] = result.concat(forObjectMap[n]);
			}
		}
		this.afterDependenceMap = result;
	}

	if (object == "*")
	{
		return this.afterDependenceMap;
	}
	else
	{
		return this.afterDependenceMap[object] || this.afterDependenceMap;
	}
};

/**
 * @public
 */
$p.buildIndirectDependenceInfos = function(directDeps)
{
	var ids = [];
	var deps = directDeps.slice(0);

	while (deps.length)
	{
		var d = deps.pop();
		var pkginf = PackageInfo.require(d.packageName);
		var f = pkginf.fileInfos[d.fileName];
		var li = f.getBeforeDependenceInfos();
		var ri = f.getAfterDependenceInfos("*");

		do
		{
			for (var i = 0; i < li.length; i++)
			{
				var d = li[i];

				if (d.path in deps || d.path in ids)
				{
					continue;
				}
				deps.push(d);
				deps[d.path] = d;

				if (!(d.path in directDeps))
				{
					ids.push(d);
					ids[d.path] = d;
				}
			}

			if (li == ri)
			{
				break;
			}
			else
			{
				li = ri;
			}
		} while (true);
	}
	return ids;
};

/**
 * @public
 */
$p.getIndirectBeforeDependenceInfos = function()
{
	if (! this.indirectBeforeDependenceInfos)
	{
		this.indirectBeforeDependenceInfos = this.buildIndirectDependenceInfos(this.getBeforeDependenceInfos());
		this.indirectBeforeDependenceInfos.sort();
	}
	return this.indirectBeforeDependenceInfos;
};


/**
 * @public
 */
$p.getIndirectAfterDependenceInfos = function(name)
{
	if (! this.indirectAfterDependenceInfos)
	{
		this.indirectAfterDependenceInfos = this.buildIndirectDependenceInfos(this.getAfterDependenceInfos(name));
		this.indirectAfterDependenceInfos.sort();
	}
	return this.indirectAfterDependenceInfos;
};
/**
 * @public
 */
$p.getIndirectObjectDependenceInfos = function()
{
	if (! this.indirectObjectDependenceInfos)
	{
		this.indirectObjectDependenceInfos = this.buildIndirectDependenceInfos(this.getObjectDependenceInfos());
		this.indirectObjectDependenceInfos.sort();
	}
	return this.indirectObjectDependenceInfos;
};
/**
 * @public
 */
$p.getDescription = function()
{
	return this.getSourceEntry().getDescription();
}
/**
 * @public
 */
$p.getSourceEntry = function()
{
	if (! this._sourceEntry)
	{
		this._sourceEntry = SourceEntry.require(this.name, this.pkg.name);
	}
	return this._sourceEntry;
};

/**
 * @public
 */
$p.getDocEntry = function(name)
{
	return this.getSourceEntry().getDocEntry(name);
};

/**
 * @public
 */
$p.getObject = function(name)
{
	return this.getObjectMap()[name];
};

/**
 * @public
 */
$p.getObjectInfo = function(name)
{
	return this.getObjectInfoMap()[name]
}

/**
 * @public
 */
$p.getObjectInfoMap = function()
{
	if (this._objectInfoMap)
	{
		return this._objectInfoMap;
	}

	try
	{
		this._objectInfoMap = {
		}

		for (var i = 0; i < this.objects.length; i++)
		{
			this._objectInfoMap[this.objects[i]] = new ObjectInfo(this, this.objects[i]);
		}
		return this._objectInfoMap;
	}
	catch (e)
	{
		$log.error(e);
	}
}

/**
 * @public
 */
$p.getObjectInfos = function()
{
	if (this._objectInfos)
	{
		return this._objectInfos;
	}
	else
	{
		this._objectInfos = [];
		var m = this.getObjectInfoMap();

		for (var n in m)
		{
			this._objectInfos.push(n);
		}
		this._objectInfos.sort();

		for (var i = 0; i < this._objectInfos.length; i++)
		{
			this._objectInfos[i] = m[this._objectInfos[i]];
		}
		return this._objectInfos;
	}
}

/**
 * @public
 */
$p.getObjectMap = function()
{
	if (this._objectMap)
	{
		return this._objectMap;
	}

	try
	{
		this._objectMap = {};

		this.pkg.loadScript(this.name, false);

		for (var i = 0; i < this.objects.length; i++)
		{
			this._objectMap[this.objects[i]] = getObject(this.objects[i], this.pkg.objectMap);
		}
	}
	catch (e)
	{
		$log.error(e);
	}
	return this._objectMap;
};

/**
 * @public
 */
$p.getAvailableObjectInfo = function(name)
{
	var file = this.getAvailableObjectFileMap()[name];

	if (file)
	{
		return file.getObjectInfo(name);
	}
}

/**
 * @public
 */
$p.getAvailableObjectMap = function(name)
{
	if (! this._availableMap)
	{
		var map = this.getAvailableObjectFileMap();
		var omap = {
		};

		for (var n in map)
		{
			omap[n] = map[n].getObject(n);
		}
		this._availableMap = omap;
	}
	return this._availableMap;
}

/**
 * @public
 */
$p.getAvailableObjectFileMap = function(name)
{
	if (! this._availableOFMap)
	{
		this._availableOFMap = {
		};

		//TODO:ååçéæ©é®é¢å¿½ç¥.
		var infos = this.getBeforeDependenceInfos();
		infos = infos.concat(this.getAfterDependenceInfos("*"));
		var fis = [];

		for (var i = 0; i < infos.length; i++)
		{
			var dp = PackageInfo.require(infos[i].packageName);
			var dep = infos[i].detail;
			var names = dep.getNames();

			for (var j = 0; j < names.length; j++)
			{
				var name = names[j];
				var file = dp.pkg.objectScriptMap[name];
				this._availableOFMap[name] = dp.fileInfos[file];
			}
		}
		var names = this.objects;

		for (var j = 0; j < names.length; j++)
		{
			this._availableOFMap[names[j]] = this;
		}
	}
	return this._availableOFMap;
}

/**
 * @public
 */
function RootInfo(bootScript)
{
	var freeScript = bootScript || function()
	{
		var scripts = document.getElementsByTagName("script");
		var freeScript = [];
		var scriptMap = {};
	
		for (var i = 0; i < scripts.length; i++)
		{
			var s = scripts[i];
	
			if (s.src)
			{
				freeScript.push(s.src);
				scriptMap[s.src] = true;
			}
		}
	
		//for mozilla document.write bug
		for (var i = 0; i < scripts.length; i++)
		{
			var s = scripts[i];
	
			while (s = s.nextSibling)
			{
				if (s.nodeType == 1)
				{
					if (s.tagName == "SCRIPT")
					{
						if (s.src)
						{
							if (scriptMap[s.src] || /__preload__\.js$/.test(s.src))
							{
								continue;
							}
							else
							{
								freeScript.push(s.src);
								scriptMap[s.src] = true;
							}
						}
					}
					else
					{
						break;
					}
				}
			}
		}
		var CommonUtil = $import("org.xidea.util.CommonUtil", null, false);
	
		return freeScript;
	}();
	this.fileInfos = [];

	for (var i = 0; i < freeScript.length; i++)
	{
		try
		{
			var info = new FreeFileInfo(this, freeScript[i]);
			this.fileInfos.push(info);
			this.fileInfos[freeScript[i]] = info;
		}
		catch (e)
		{
		}
	}
	this.fileInfos.sort();
	this.name = "";
	this.dependences = [];
}

$p = RootInfo.prototype;

/**
 * @public
 */
$p.getAvailableObjectFileMap = function()
{
	if (! this._availableOFMap)
	{
		this._availableOFMap = {};

		var fis = this.fileInfos;

		for (var i = 0; i < fis.length; i++)
		{
			var os = fis[i].objects;

			for (var j = 0; j < os.length; j++)
			{
				this._availableOFMap[os[j]] = fis[i];
			}
		}
	}
	return this._availableOFMap;
};
/**
 * @public
 */
$p.getDocEntry = function(name)
{
	for (var i = this.fileInfos.length - 1, d; i >= 0; i--)
	{
		d = this.fileInfos[i]._sourceEntry.getDocEntry(name);

		if (d)
		{
			return d;
		}
	}
};
Type.extend($p, PackageInfo.prototype, false);

/**
 * @public
 */
function FreeFileInfo(rootInfo, fileName)
{
	this.name = fileName;
	this.pkgInfo = rootInfo;
	this.objects = [];
	this.dependences = [];
	this._sourceEntry = SourceEntry.require(fileName);
	this.topDocEntries = this._sourceEntry.getTopDocEntries();

	for (var i = 0; i < this.topDocEntries.length; i++)
	{
		var name = this.topDocEntries[i].getId();
		this.objects.push(name);
	}
}

$p = FreeFileInfo.prototype;

/**
 * @public
 */
$p.getObjectInfoMap = function()
{
	if (this._objectInfoMap)
	{
		return this._objectInfoMap;
	}

	this._objectInfoMap = {};

	try
	{
		for (var i = 0; i < this.objects.length; i++)
		{
			this._objectInfoMap[this.objects[i]] = new ObjectInfo(this, this.objects[i]);
		}
		return this._objectInfoMap;
	}
	catch (e)
	{
		$log.error(e);
	}
}

/**
 * @public
 */
$p.getAvailableObjectFileMap = function()
{
	return this.pkgInfo.getAvailableObjectFileMap();
}

/**
 * @public
 */
$p.getObjectMap = function()
{
	if (this._objectMap)
	{
		return this._objectMap;
	}

	try
	{
		this._objectMap = {};

		for (var i = 0; i < this.objects.length; i++)
		{
			this._objectMap[this.objects[i]] = Core[this.objects[i]] || window[this.objects[i]];
		}
	}
	catch (e)
	{
		$log.error(e);
	}
	return this._objectMap;
}

/**
 * @public
 */
$p.getDocEntry = function(name)
{
	return this.pkgInfo.getDocEntry(name);
};

/**
 * @public
 */
$p.getObject = function(name)
{
	return this.getObjectMap()[name];
}

/**
 * @public
 */
 Type.extend($p, FileInfo.prototype, false);
//buildExtension(FreeFileInfo, FileInfo);

$p = null;

var Core = {
	"ScriptLoader" : this.constructor
	, "Package" : $import("js.lang:").constructor
	, "Log" : $log.constructor
};

/**
 * @public
 */
function getObject(name, map)
{
	var name = name.split(".");

	for (var i = 0; map != null && i < name.length; i++)
	{
		map = map[name[i]];
	}
	return map;
}